Drought & canopy stress • NDVI & LST space

TVX – Temperature–Vegetation Index (example)

TVX represents the relationship between vegetation greenness (NDVI) and land-surface temperature (LST) to indicate canopy water / heat stress. Here it is implemented as an example index combining normalised NDVI and LST in a simple way for drought and stress analysis.


TVX overview concept & use-cases (example)

What does TVX measure?

In the temperature–vegetation (TVX) framework, healthy, well-watered vegetation tends to be green and relatively cool (high NDVI, low LST), while stressed vegetation tends to be less green and hotter. TVX captures this contrast by combining normalised NDVI and LST into one index.

  • Qualitative drought and crop-stress mapping.
  • Screening water-limited vs. well-watered agricultural areas.
  • Supporting TVDI / VSWI / VHI-style analyses as an additional view.
  • Providing a quick, intuitive NDVI–LST stress indicator.

Important: There is no single, universal algebraic formula for a TVX index. The expression below is a practical example. You can replace it with your preferred TVX / TVDI formulation while keeping this HTML layout.

TVX formula (example)

Normalised NDVI & LST
NDVIn = (NDVI − NDVImin) / (NDVImax − NDVImin)
Tsn = (Ts − Tsmin) / (Tsmax − Tsmin)

TVX = Tsn − NDVIn

With this definition, high TVX values indicate relatively hot and/or low-NDVI conditions (higher stress), while low or negative TVX indicates cool, green conditions (lower stress).

You may also scale TVX to 0–100, or invert / shift it so that higher values correspond to healthier vegetation, depending on your convention.

Required inputs

Spectral & thermal components

Component Source
NDVI RED & NIR bands (Landsat, Sentinel-2, MODIS, etc.)
Ts / LST Thermal band (Landsat) or LST product (MODIS, etc.)
NDVImin/max Derived over ROI / season or reference period
Tsmin/max Derived over ROI / season or reference period

Tip: TVX is more robust when NDVI and LST are derived from cloud-free, atmospherically corrected imagery and when reference min/max values are computed over a representative period.

Interpreting TVX (example)

TVX value Interpretation (relative)
High positive Hot & low NDVI – high stress / drought
Moderate Intermediate conditions – mild to moderate stress
Near zero or negative Cool & relatively green – low stress / well-watered
Very low / noisy Bare soil / non-vegetated / mixed pixels

Exact ranges are project-specific. Calibrate TVX against field observations or known drought events for your region.

Using TVX in Google Earth Engine (example with Landsat 8/9)

  1. Compute NDVI and LST / Ts from Landsat or another sensor.
  2. Derive NDVImin/max and Tsmin/max over ROI / season.
  3. Normalise NDVI and Ts to [0–1].
  4. Compute TVX = Tsn − NDVIn and map relative stress.
// TVX – Temperature–Vegetation Index example (Landsat 8/9) in Google Earth Engine
var roi = /* your geometry here */;

// Landsat 8/9 L2 collection (surface reflectance + surface temperature)
var l8 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
  .filterBounds(roi)
  .filterDate('2023-04-01', '2023-09-30')
  .filter(ee.Filter.lt('CLOUD_COVER', 20));

// Scale function for optical & thermal bands
function scaleL8(img) {
  var optical = img.select(['SR_B.']).multiply(0.0000275).add(-0.2);
  var ts = img.select('ST_B10').multiply(0.00341802).add(149.0); // Kelvin
  return img.addBands(optical, null, true)
            .addBands(ts.rename('Ts'));
}

l8 = l8.map(scaleL8);

// Median composite
var img = l8.median();

// NDVI
var nir  = img.select('SR_B5');
var red  = img.select('SR_B4');
var ndvi = nir.subtract(red).divide(nir.add(red)).rename('NDVI');

// Surface temperature (Kelvin)
var ts = img.select('Ts');

// Derive min/max for NDVI and Ts over ROI
var ndviStats = ndvi.reduceRegion({
  reducer: ee.Reducer.minMax(),
  geometry: roi,
  scale: 30,
  maxPixels: 1e7
});

var tsStats = ts.reduceRegion({
  reducer: ee.Reducer.minMax(),
  geometry: roi,
  scale: 30,
  maxPixels: 1e7
});

var ndviMin = ee.Number(ndviStats.get('NDVI_min'));
var ndviMax = ee.Number(ndviStats.get('NDVI_max'));
var tsMin   = ee.Number(tsStats.get('Ts_min'));
var tsMax   = ee.Number(tsStats.get('Ts_max'));

// Normalised NDVI and Ts
var ndvi_n = ndvi.subtract(ndviMin)
  .divide(ndviMax.subtract(ndviMin).add(1e-6));

var ts_n = ts.subtract(tsMin)
  .divide(tsMax.subtract(tsMin).add(1e-6));

// TVX = Ts_n - NDVI_n
var tvx = ts_n.subtract(ndvi_n).rename('TVX');

// Visualisation (example palette: low = cool/green, high = hot/stressed)
Map.centerObject(roi, 8);
Map.addLayer(tvx, {
  min: -1.0, max: 1.0,
  palette: ['#0f766e','#22c55e','#eab308','#f97316','#7f1d1d']
}, 'TVX - Temperature–Vegetation Index');

// Optional export
Export.image.toDrive({
  image: tvx,
  description: 'TVX_Landsat8_example',
  region: roi,
  scale: 30,
  maxPixels: 1e13
});

Important: treat this TVX definition as a practical example. If you use a specific TVX / TVDI / NDVI–LST relationship from a paper, simply replace the equations while keeping this HTML structure in your indices library.

Part of the Start4IT Remote Sensing Indices Library. More indices & ready-to-use code: www.start4it.com/rs-indices